package lib.rpc;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.UUID;

import lib.ArgumentValidator;
import lib.messaging.NetworkAddress;

/**
 * Represents the result of RPC invocation Provides marshalling as well to
 * send/receive the packet over wire
 *
 * @author abhip
 *
 */
public class RPCResultPacket implements Serializable {
    /**
     * serialVersionUID is a universal version identifier for a Serializable
     * class. Deserialization uses this number to ensure that a loaded class
     * corresponds exactly to a serialized object. If no match is found during
     * deserialization, an InvalidClassException is thrown.
     */
    private static final long serialVersionUID = -378386610921486102L;

    /**
     * Deserializes RPCResultPacket from byte array
     * @param data
     * @return deserialized RPCResultPacket
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static RPCResultPacket Deserialize(byte[] data) throws IOException, ClassNotFoundException
    {
        ObjectInputStream objectInputStream = new ObjectInputStream(
                new ByteArrayInputStream(data));

        Object obj = objectInputStream.readObject();
        if (obj instanceof RPCResultPacket)
        {
            return (RPCResultPacket) obj ;
        }

        throw new IOException(
                String.format(
                        "Could not deserialize RPCResultPacket! Got packet of type: %s, Packet details: %s",
                        obj.getClass(),
                        obj.toString()));
    }

    private UUID requestID;
    private NetworkAddress responseServerAddress;
    private Object result;
    private RPCResultCode resultCode;

    public RPCResultPacket(
            UUID requestID,
            NetworkAddress responseServerAddress,
            RPCResultCode resultCode,
            Object result)
    {
        ArgumentValidator.IsNotNull("requestID", requestID);
        ArgumentValidator.IsNotNull("responseServerAddress", responseServerAddress);
        ArgumentValidator.IsNotNull("resultCode", resultCode);
        // Skipping null check for result as it can be null (eg, when RPC method
        // invoked was of type void).

        this.requestID = requestID;
        this.responseServerAddress = responseServerAddress;
        this.resultCode = resultCode;
        this.result = result;
    }

    /**
     * Getter for sequenceID
     *
     * @return sequenceID
     */
    public UUID RequestID() {
        return requestID;
    }

    public NetworkAddress responseServerAddress()
    {
        return responseServerAddress;
    }

    /**
     * Getter for result
     *
     * @return result
     */
    public Object Result() {
        return result;
    }

    /**
     * Getter for resultCode
     *
     * @return resultCode
     */
    public RPCResultCode ResultCode() {
        return resultCode;
    }

    /**
     * Serializes RPCResultPacket to byte array
     *
     * @return serialized byte array
     * @throws IOException
     */
    public byte[] Serialize() throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(
                byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        objectOutputStream.flush();
        return byteArrayOutputStream.toByteArray();
    }

    /**
     * String representation of RPCResultPacket
     *
     * @return
     */
    @Override
    public String toString() {
        return String
                .format("requestID: %s, responseServer: %s, resultCode: %s, result: %s",
                        requestID,
                        responseServerAddress.toString(),
                        resultCode,
                        result);
    }
}
